// Game.cpp //
#include "Game.h"
#include "SDL.h"
#include <iostream>
#include<time.h>


#include "Config.h"
#include "Sound.h"
#include "Level.h"
#include "Player.h"
#include "Boss.h"

int divide = 0; // assigns some value to global divide value

//Game class is the games main running loop and controls initialization
//destruction and running of main game. 


Game::Game() // constructor initializes all sdl items
{
    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
    {
        fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
        exit(1);
	}

    SDL_WM_SetCaption("Educational Shooter", NULL);
    SDL_ShowCursor(SDL_DISABLE);
    
    if(TTF_Init() < 0) 
    {
        fprintf(stderr, "Unable to initialize TTF: %s\n", SDL_GetError());
        exit(1);
	}

    if(Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 1024) < 0)
    {
        fprintf(stderr, "Unable to initialize audio: %s\n", SDL_GetError());
        exit(1);
	}
    
	screen = SDL_SetVideoMode(ScreenWidth, ScreenHeight, ScreenBPP, SDL_HWSURFACE | SDL_DOUBLEBUF);
	
	if(screen == NULL)
	{
		fprintf(stderr, "Failed to initialize the screen.\n");
		exit(1);
	}
	
	done = false;
	frame = 0;
	newDivisor = 0;
	gamestate = STATE_MENU; // initialize state to main menu
	singlePlayer = true; // initially implemented with 2 player possible in mind
	showDebug = false; 
}

Game::~Game() // basic destructor
{
    Mix_CloseAudio();
    TTF_Quit();
	SDL_Quit();
	printf("Game ended");
}

void Game::OnExecute() // controls main game loop
{
	OnStart();
	
    while(!done) // three part loop controls game
    {
        CheckEvents();
		OnUpdate();
		OnRender();
    }
    
    OnEnd();
}

void Game::OnStart() // loads sounds, textures, and starts menu music
{
	srand ( time(NULL) );
	Texture::LoadTextures();
	Sound::LoadSounds();
    Sound::PlaySound("Intro");
}

void Game::OnEnd() // unloadds files
{
	Texture::UnloadTextures();
	Sound::UnloadSounds();
	Player::RemovePlayers();
	Object::RemoveAll();
}


//controls game state, checks for all external events
void Game::CheckEvents() 
{
	while(SDL_PollEvent(&event))
    {
		if(gamestate == STATE_MENU)
		{
            if(event.type == SDL_KEYDOWN)
            {
                if(event.key.keysym.sym == SDLK_RETURN) // start game
                {
					divide = (rand()%11+2);
                    gamestate = STATE_PLAYING;
					Level::LevelOne.BuildLevel();
					Player::AddPlayers();
					Player::SpawnPlayer(PLAYER_ONE);
                    Sound::PlaySound("Theme");
				}
			}
		}
		//when an end game screen is present
		else if(gamestate == STATE_GAMEOVER || gamestate == STATE_VICTORY)
		{
			if(event.type == SDL_KEYDOWN)
			    if(event.key.keysym.sym == SDLK_RETURN)
			    {
			        gamestate = STATE_MENU;
					Player::RemovePlayers();
					Boss::RemoveBoss();
					Object::RemoveAll();
        			Sound::PlaySound("Intro");
				}
		}
		else if(gamestate == STATE_PLAYING)
		{
            Player::ProcessInput(event); // gets game player events
            
            if(event.type == SDL_KEYDOWN)
            {
                if(event.key.keysym.sym == SDLK_RETURN)
                    gamestate = STATE_PAUSED;
			}
		}
		else if(gamestate == STATE_PAUSED)
		{
            if(event.type == SDL_KEYDOWN)
                if(event.key.keysym.sym == SDLK_RETURN)
                    gamestate = STATE_PLAYING;
		}

        if(event.type == SDL_KEYDOWN) // events that can occur in any state
        {
			if(event.key.keysym.sym == SDLK_ESCAPE)
				done = true;
			if(event.key.keysym.sym == SDLK_TAB)
			    showDebug = !showDebug;
		}
				
		if(event.type == SDL_QUIT)
			done = true;
    }
}
//updates game based on change in time, ie moves enemies, bullets, player, etc
void Game::OnUpdate()
{
	float deltaTime = timer.getDeltaTime();
	
	if(gamestate == STATE_PLAYING)
	{
		//following lines control the change in the master divisor
		newDivisor+=deltaTime;	
		if(newDivisor > DivisorDelay)
		{
			divide = (rand()%11+2);
			newDivisor = 0;
		}
		//object update updates objects, ie players, bullers, enemies
		Object::UpdateObjects(deltaTime);
		//level update checks level file and spawns enemies
		Level::LevelOne.Update(deltaTime);
		//starfield creates the background
		starfield.Update(deltaTime);
		
		if(Player::Players[0].lives <= 0) // if player died
		{
		    gamestate = STATE_GAMEOVER;
        	Sound::PlaySound("GameOver");
		}	
		if(Boss::FinalBoss.spawned && Boss::FinalBoss.killed) // if boss died
		{
		    gamestate = STATE_VICTORY;
        	Sound::PlaySound("Victory");
		}
	}
	
	++frame;
}

void Game::OnRender() // renders everything to screen
{
    SDL_FillRect(screen, NULL, 0);// creates black screen
    
    if(gamestate == STATE_MENU) // renders menu
	{
		DrawTitle();
	}
	else if(gamestate == STATE_PLAYING) 
	{
		// all objects written at once, some override default render
		Object::RenderObjects(screen);
		DrawHUD(); // draws the hud
	}
	else if(gamestate == STATE_PAUSED) // paused screen
	{
		Object::RenderObjects(screen);
		DrawHUD();
		DrawPaused();
	}
	else if(gamestate == STATE_GAMEOVER) // game over
	{
		Object::RenderObjects(screen);
		DrawHUD();
		DrawGameOver();
	}
	else if(gamestate == STATE_VICTORY) // victory, still pretty self explanatory
	{
		Object::RenderObjects(screen);
		DrawHUD();
		DrawVictory();
	}
	
	if(showDebug) // will show some debug info
		DrawDebug();
    
    SDL_Flip(screen);// flip the double buffered screen
}

// helper functions to draw various parts of the game

void Game::DrawHUD()
{
	// display health and lives
    SDL_Rect health1 = {ScreenWidth-220, 45, 200, 10};
    SDL_FillRect(screen, &health1, SDL_MapRGB(screen->format, 50, 50, 50));

    if(Player::Players[0].ship != NULL)
	{
		health1.w = Player::Players[0].ship->health * 2;
    	SDL_FillRect(screen, &health1, SDL_MapRGB(screen->format, 200, 50, 50));
	}

    for(int i = 0; i < Player::Players[0].lives; ++i)
    {
		SDL_Rect box = {ScreenWidth-240+(i + 1) * 20, 25, 10, 10};
		SDL_FillRect(screen, &box, SDL_MapRGB(screen->format, 255, 255, 255));
	}

	// display score
	char buffer[20];
	sprintf(buffer, "%d", Player::Players[0].score);
	Texture::DrawText(screen, "SCORE", 10, ScreenWidth / 2, ScreenHeight - 35, 255, 255, 255);
	Texture::DrawText(screen, buffer,  14, ScreenWidth / 2, ScreenHeight - 20, 255, 255, 255);

	// display divisor
	sprintf(buffer, "Divide by %d!", divide);
	Texture::DrawText(screen, buffer, 24, 10, 10, 255, 255, 255,false);

	//show boss health
	if(Boss::FinalBoss.spawned && !Boss::FinalBoss.killed)
	{
		int barWidth = ScreenWidth - 40;
		SDL_Rect boss = {20, 40, barWidth, 10};
    	SDL_FillRect(screen, &boss, SDL_MapRGB(screen->format, 50, 50, 50));

    	if(Boss::FinalBoss.boss != NULL)
    	{
			boss.w = (Sint16)(1.0f * Boss::FinalBoss.boss->health / BossHealth * barWidth);
    		SDL_FillRect(screen, &boss, SDL_MapRGB(screen->format, 50, 200, 50));
		}
	}
}
//this is the menu screen
void Game::DrawTitle()
{
	SDL_Surface* titleSurf = Texture::TextureList["Title"]->textures[0];
	SDL_Rect titleRect = {ScreenWidth / 2 - titleSurf->w / 2, ScreenHeight / 2 - titleSurf->h / 2 - 100};
	SDL_BlitSurface(titleSurf, NULL, screen, &titleRect);

	Texture::DrawText(screen, "Press ENTER to Start", 25, ScreenWidth / 2, ScreenHeight / 2 + 75, 255, 255, 255);
	Texture::DrawText(screen, "Exit",     20, ScreenWidth / 2 - 100, ScreenHeight / 2 + 150, 255, 255, 255);
	Texture::DrawText(screen, "(Esc)",    20, ScreenWidth / 2 + 100, ScreenHeight / 2 + 150, 255, 255, 255);
}

//overlays a transparent blue screen on the current game screen
void Game::DrawPaused()
{
	SDL_Rect rect = {0, 0, ScreenWidth, ScreenHeight};
	Texture::ColoredRect(screen, rect, 0, 50, 80, 128);
	Texture::DrawText(screen, "PAUSED", 30, ScreenWidth / 2, ScreenHeight / 2, 255, 255, 255);
}

// displays game over screen over the current gme screen
void Game::DrawGameOver()
{
	SDL_Rect rect = {0, 0, ScreenWidth, ScreenHeight};
	Texture::ColoredRect(screen, rect, 100, 100, 150, 128);
	Texture::DrawText(screen, "Try Again", 95, ScreenWidth / 2, ScreenHeight / 2, 0, 0, 250);
	Texture::DrawText(screen, "Press ENTER to Return to Main Menu", 16, ScreenWidth / 2, ScreenHeight / 2 + 70, 0, 0, 250);
}

//displays victory over current screen
void Game::DrawVictory()
{
	SDL_Rect rect = {0, 0, ScreenWidth, ScreenHeight};
	Texture::ColoredRect(screen, rect, 0, 255, 0, 128);
	Texture::DrawText(screen, "SUCCESS", 95, ScreenWidth / 2, ScreenHeight / 2, 0, 255, 0);
	Texture::DrawText(screen, "Press ENTER to Return to Main Menu", 16, ScreenWidth / 2, ScreenHeight / 2 + 50, 0, 255, 0);
}

// debug info, just number of objects, and current FPS
void Game::DrawDebug()
{
	char buffer[15];
	sprintf(buffer, "FPS: %d", (int)(frame / timer.getStartTime()));
	Texture::DrawText(screen, buffer, 14, 10, 10, 255, 255, 255, false);
	sprintf(buffer, "Objects: %d", Object::ObjectList.size());
	Texture::DrawText(screen, buffer, 14, 10, 30, 255, 255, 255, false);
}
